<?php

namespace App\Model;

use Hyperf\Collection\Collection;
use Hyperf\Context\ApplicationContext;
use Hyperf\Database\Model\Relations\BelongsTo;
use Hyperf\Database\Model\Relations\BelongsToMany;
use Hyperf\Utils\Arr;
use Hyperf\Redis\Redis;
use Carbon\Carbon;
use App\Services\Iot\IotService1 as IotService;
use PHPUnit\Util\Test;

/**
 * App\Device
 *
 * @property int $id
 * @property string $sn 设备编号
 * @property string|null $iot_sn 主机才有的 iot sn
 * @property int $channel 路数
 * @property int|null $station_id 充电站id
 * @property int $orders_count 订单数
 * @property int $inline 在线
 * @property int|null $use_rate 使用率
 * @property array|null $other 其他数据
 * @property string|null $qr_code 二维码
 * @property-read int|null $distributors_count
 * @property array|null $port_status 端口状态
 * @property int $is_purchase 是否为买断
 * @property int $is_active 是否可用
 * @property-read int|null $daily_records_count
 * @property float|null $deposit 押金
 * @property-read int|null $coupons_count
 * @property string|null $card_no 卡号
 * @property int $signal 信号强度
 * @property string|null $coin_min 投币充电时间(单位为分钟)
 * @property string|null $card_min 刷卡充电时间(单位为分钟)
 * @property string|null $coin_elec 单次投币最大电量(单位为0.1度)
 * @property string|null $card_elec 单次刷卡最大电量(单位为0.1度)
 * @property string|null $cst 刷卡扣费金额(单位为角)
 * @property string|null $power_max_1 第1档最大充电功率(单位为瓦)
 * @property string|null $power_max_2 第2档最大充电功率(单位为瓦)
 * @property string|null $power_max_3 第3档最大充电功率(单位为瓦)
 * @property string|null $power_max_4 第4档最大充电功率(单位为瓦)
 * @property string|null $power_2_time 第2档充电时间百分比
 * @property string|null $power_3_time 第3档充电时间百分比
 * @property string|null $power_4_time 第4档充电时间百分比
 * @property string|null $sp_rec_mon 是否支持余额回收
 * @property string|null $sp_full_empty 是否支持断点自停
 * @property string|null $full_power_min 最大浮冲功率(单位为瓦)
 * @property string|null $full_charge_time 浮冲时间(单位为分钟)
 * @property string|null $elec_time_first 是否初始显示剩余电量
 * @property int $is_fault 是否故障
 * @property-read int|null $fault_logs_count
 */
class Device extends Model
{
    const SIGNAL_TIME_OUT = 'signal_time_out';
    //
    public bool $timestamps = true;
    protected ?string $table = 'devices';

    private const GEO_KEY = 'devices:geo';

    protected array $fillable = [
        'id', 'sn','iot_sn','channel','qr_code','station_id',
        'orders_count','active_at','expires_at','inline','port_status',
        'use_rate','is_purchase','can_refund_deposit_at','refund_deposit_at',
        'is_active','other','last_online_at','deposit','card_no',
        'signal','params_read_at','coin_min','card_min','coin_elec',
        'card_elec','cst','power_max_1','power_max_2','power_max_3',
        'power_max_4','power_2_time','power_3_time','power_4_time',
        'sp_rec_mon','sp_full_empty','full_power_min','full_charge_time',
        'elec_time_first','is_fault','only_money','charge_id',
        'distributor_id','child_distributor_id','is_refund','remark','is_disabled',
        'is_service'
    ];

    protected array $casts = [
//        'inline' => 'boolean',
//        'is_active' => 'boolean',
//        'is_purchase' => 'boolean',
        'last_online_at' => 'datetime',
        'active_at' => 'datetime',
        'expires_at' => 'datetime',
        'created_at'=>'datetime',
        'can_refund_deposit_at' => 'datetime',
        'refund_deposit_at' => 'datetime',
        'params_read_at' => 'datetime',
        'other' => 'json',
        'port_status' => 'json',
    ];

    const CHANNEL_MAX = 10;
//    const JOIN_DEPOSIT_FEE = 200;

    const RENEW_FEE = 25.00;


    public static array $otherFieldMap = [
        'n' => 'SIM卡CCID号',
        'v' => '主机软件版本',
        'm' => '主机通讯模块型号',
    ];

    /**
     * @return float
     */
    public function getRenewFee():float
    {
        return self::RENEW_FEE;
    }


    public function purchaseRenew($year = 1)
    {
        $expiresAt = $this->expires_at ?? Carbon::now();
        $expiresAt = Carbon::now()->max($expiresAt);

        $this->expires_at = $expiresAt->addYears($year);
        $this->is_active = true;
        $this->save();
    }

    public function isExpiring()
    {
        return $this->is_purchase && $this->expires_at && $this->expires_at->subMonth()->lt(now());
    }

    public function scopeIsExpired($query)
    {
        return $query->where('is_purchase', true)->where('expires_at', '<', now());
    }

    public function scopeOnlyMoney($query)
    {
        return $query->where('only_money', true);
    }

    public static function findOrFailViaSn($sn)
    {
        return static::where('sn', $sn)->firstOrFail();
    }


    public function reset()
    {
        $this->distributors()->sync([]);
        $this->station_id = null;
        $this->is_active = true;
        $this->active_at = null;
        $this->expires_at = null;
        $this->is_purchase = true;
        $this->save();
    }

    /**
     * @param array $data
     * @param $mqtt
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function iotEventHandle(array $data)
    {
        $len = Arr::get($data, 1);
        //len不对号就是分包了需要打一下包
        if(count($data) != $len + 2) {
//            logger('data joining');
            $key = "join_package_" . $this->iot_sn;
            $cacheData = redis()->get($key) ?? [];
            $data = array_merge($cacheData, $data);
            //重新取长度
            $len = Arr::get($data, 1);
            //打包后长度依然不对 就缓存5秒 并退出处理逻辑 留给下次
            if(count($data) != $len + 2) {
                //包只存5秒
                redis()->setex($key,5, json_encode($data));
//                logger('data not full');
//                Normal::logger('iotEventHandlelog_cache ' . json_encode($data),'iotevent','info');
                return;
            }

            //包已经完整了
//            logger('join package done ', $data);
        }

        IotLog::record($this->iot_sn, IotLog::TYPE_RECEIVE, $data);
        //更新在线状态
        $this->last_online_at = Carbon::now();
        $this->save();
        if(empty($this->active_at)) {
            $this->changeToActive();
        }
        $cmd = Arr::get($data, 2);
        $cmdStatus = Arr::get($data, 3);
        switch ($cmd)
        {
            case 0x04:
                $port = Arr::get($data, 4);
                $order = $this->orders()->where('status', Order::STATUS_PAID)->where('channel_number', $port)->latest()->first();
                if(!$order) {
                    $order = $this->orders()->where('status', Order::STATUS_USING)->where('channel_number', $port)->latest()->first();
                    if(!$order){
                        Normal::logger('iotEventHandle error order not found' . $this->iot_sn,'iotevent','error');
                        return;
                    }else{
                        break;
                    }
                }

                if($cmdStatus != 0x01) {
                    $order->fail('通讯失败');
                    return;
                }

                $status = Arr::get($data, 5);

                if($port >= 10 && !$status && count($data) < 7) {
                    $status = 0x01;
                }

                switch ($status)
                {
                    //成功
                    case 0x01:
                        $order->startSuccess();
                        break;

                    case 0x0B:
                        $order->fail("充电桩端口故障");
                        $this->incrementOnceFault("充电桩端口故障");
                        break;
                    case 0x0C:
                        $order->fail("端口已经被使用");
                        break;
                    default:
                        $order->fail("未知错误 RESULT: " . $status);
                        $this->incrementOnceFault("未知错误 RESULT: " . $status);
                        break;
                }

                break;
            case 0x15:
                $port = Arr::get($data, 4);
                $power =  Arr::get($data, 8);
                $time =  Arr::get($data, 6);
//              $order = $this->orders()->where('status', Order::STATUS_PAID)->where('channel_number', $port)->latest()->first();
                break;
            //充电结束
            case 0x16:
                $port = Arr::get($data, 4);
                //回复会造成设备报语音错误
//                IotService::replayStop($this->iot_sn, $port);
                $order = $this->orders()->where('status', Order::STATUS_USING)->where('channel_number', $port)->latest()->first();
                if(!$order) {
                    Normal::logger('iotEventHandle error order not found ' . $this->iot_sn,'iotevent','error');
                    return;
                }

                if($cmdStatus != 0x01) {
                    Normal::logger('iotEventHandle error $cmdStatus  ' . $cmdStatus,'iotevent','error');
                    return;
                }

                $reasonCode = Arr::get($data, 9, 99);

                $isError = false;
                switch ($reasonCode)
                {
                    case 0x00:
                        $reason = "充电完成";
                        break;
                    case 0x01:
                        $reason = "超时未充电或用户手动停止";
                        break;
                    case 0x02:
                        $reason = "充满";
                        break;
                    case 0x03:
                        $reason = "超功率自停";
                        break;
                    case 0x04:
                        $reason = "远程断电";
                        break;
                    case 0x0B:
                        $isError = true;
                        $reason = "设备或端口故障";
                        $this->incrementOnceFault($reason);
                        break;
                    default:
                        $isError = true;
                        $reason = "未知故障 CODE:" . $reasonCode;
                        $this->incrementOnceFault($reason);
                }

                $isError = $isError || $order->getDurationSeconds() <= Order::FREE_SECONDS;
                if($isError) {
                    $order->fail($reason);
                } else {
                    //2个16进制数表示的电量 1表示0.01度
                    $surplusElectric = Arr::get($data, 7)  * 256 + Arr::get($data, 8);
                    //转换
                    $surplusElectric = round($surplusElectric / 100, 2);

                    $order->stop($reason, $surplusElectric);
                }
                break;

            //上传设备端口状态
            case 0x21:
                $portNumber = Arr::get($data, 4);
                $statusMap = [];
                //去掉前5个参数 然后每个端口会占用8个 取出所有端口数据 再加工
                collect($data)
                    ->filter(function ($value, $key) {
                        //跳过前5个数据
                        return $key > 4;
                    })
                    ->take($portNumber * 8)
                    ->chunk(8)
                    ->map(function (\Hyperf\Utils\Collection $collection) use (&$statusMap) {
                        $collection = $collection->values();
                        $port = $collection->get(0);
                        $item = [
                            'number' => $port,
                            //1正常 2使用中 3禁用  4故障
                            'status' => $collection->get(1),
                            'time' => $collection->get(2) * 256 + $collection->get(3),
                            'power' => $collection->get(4) * 256 + $collection->get(5),
                            'electric_quantity' => $collection->get(6) * 256 + $collection->get(7),
                        ];

                        //0端口号 1状态 23时间 45实时功率 67剩余电量
                        $statusMap[$port] = $item;
                    })->toArray();
                $this->channel = $portNumber;
                $this->port_status = $statusMap;
                $this->save();
                // 取出是否有正在充电中的订单
                $orders = Order::where('device_id', $this->id)->where('status', Order::STATUS_USING)->get();
                foreach ($orders as $order) {
                    if ($order->power <= 0) {
                        $order->power = $statusMap[$order->channel_number]['power'];
                        $order->save();
                    }
                }
                break;
            //卡号上报
            case 0xA1:

                $cardNo = collect($data)
                    ->filter(function ($value, $key) {
                        //跳过前4个数据
                        return $key > 3;
                    })

                    ->take(20)
                    ->map(function ($value) {
                        return chr($value);
                    })
                    ->implode('');

//                Normal::logger('device card no:' . $cardNo,'iotevent');

                if($this->card_no != $cardNo) {
                    $this->card_no = $cardNo;
                    $this->save();
                }

                break;

            //信号上报
            case 0xA2:

                $signal = collect($data)
                    ->filter(function ($value, $key) {
                        //跳过前4个数据
                        return $key > 3;
                    })
                    ->take(1)
                    ->first();

                logger('device ' . $this->iot_sn . ' signal: ' . $signal);

                $this->signal = (int) $signal;
                $this->inline = 1;
                $this->save();

                $log = new DeviceSignalLog([
                    'signal' => $signal
                ]);
                $log->device()->associate($this);
                $log->save();
                $cacheKey =self::SIGNAL_TIME_OUT;
                $now = time();
                redis()->zAdd($cacheKey,$now,$this->iot_sn);
                //IotService::queryDeviceStatus($this->iot_sn);
                IotService::replaySomeThing($this->iot_sn);

                break;

            //配置读取上报
            case 0x1E:

                $params = collect($data)
                    ->filter(function ($value, $key) {
                        //跳过前4个数据
                        return $key > 3;
                    })
                    ->values()
                    ->take(23)
                    ->toArray();

                $this->params_read_at = Carbon::now();
                $this->coin_min = $params[0] * 256 + $params[1];
                $this->card_min = $params[2] * 256 + $params[3];
                $this->coin_elec = $params[4];
                $this->card_elec = $params[5];
                $this->cst = $params[6];
                $this->power_max_1 = $params[7] * 256 + $params[8];
                $this->power_max_2 = $params[9] * 256 + $params[10];
                $this->power_max_3 = $params[11] * 256 + $params[12];
                $this->power_max_4 = $params[13] * 256 + $params[14];
                $this->power_2_time = $params[15];
                $this->power_3_time = $params[16];
                $this->power_4_time = $params[17];
                $this->sp_rec_mon = $params[18];
                $this->sp_full_empty = $params[19];
                $this->full_power_min = $params[20];
                $this->full_charge_time = $params[21];
                $this->elec_time_first = $params[22];
                $this->save();

//                //计算耗电量
//                $notSettlementElecOrders = $this->orders()->whereNull('electric_quantity')->get();
//                $notSettlementElecOrders->map(function (Order $order) {
//                    //单次刷卡最大用电量 单位为0.1/元 减去剩余电量则为实际电量(传入的电量实际为剩余电量) 所以销售的电量减去剩余电量就是实际使用的电量
//                    $realElectric = $this->card_elec * 0.1 * $order->original_fee  - $order->surplus_electricity;
//                    $order->electric_quantity = max($realElectric, 0);
//                    $order->save();
//                });

                break;
            default:
                logger('data not handler ' . $cmd);
        }
    }


    //增加一次故障记录
    public function incrementOnceFault($reason)
    {
        $key = 'device_fault_' . $this->id;
        if(redis()->exists($key)){
            $times = redis()->get($key);
        }else{
             redis()->setex($key,60*10,0);
             $times = 0;
        }
        redis()->incr($key);
        try{
            if($times >= 3) {
                $deviceFaultLog = new DeviceFaultLog();
                $deviceFaultLog->device_id = $this->id;
                $deviceFaultLog->remark = $reason;
                $deviceFaultLog->save();
                $this->is_fault = true;
                $this->save();
                //清除故障记录
                redis()->del($key);
            }
        }catch (\Exception $e){

        }
    }



    public function readParams()
    {
        IotService::readParams($this->iot_sn);
    }

    public function updateParams()
    {
        IotService::updateParams($this->iot_sn, [
            floor($this->coin_min / 256),
            $this->coin_min % 256,

            floor($this->card_min / 256),
            $this->card_min % 256,

            $this->coin_elec,
            $this->card_elec,
            $this->cst,

            floor($this->power_max_1 / 256),
            $this->power_max_1 % 256,

            floor($this->power_max_2 / 256),
            $this->power_max_2 % 256,

            floor($this->power_max_3 / 256),
            $this->power_max_3 % 256,

            floor($this->power_max_4 / 256),
            $this->power_max_4 % 256,

            $this->power_2_time,
            $this->power_3_time,
            $this->power_4_time,
            $this->sp_rec_mon,
            $this->sp_full_empty,
            $this->full_power_min,
            $this->full_charge_time,
            $this->elec_time_first,
        ]);
    }



    /**
     * @param User $user
     * @return mixed
     */
    public function getCanReceiveCoupons(User $user)
    {

        $station = $this->station;
        if(!$station) {
            abort(400, '设备未绑定站点');
        }
//
//        $distributor = $station->distributor;

        if($user->couponRecords()->exists()) {
            return collect([]);
        }

        $receivedCouponIds = $user->couponRecords->pluck('coupon_id')->toArray();

        return $this
            ->coupons()
            ->newUser()
            ->isActive()
            ->effective()
            ->withCount(['records'])
            ->whereNotIn('coupons.id', $receivedCouponIds)
            ->get()
            ->filter(function (Coupon $coupon) use ($user) {
                //过滤掉不能领取的
                return $coupon->checkCanReceive($user);
            });
    }

    public function coupons()
    {
        return $this->belongsToMany(Coupon::class, 'coupon_devices');
    }

    public function ad():BelongsToMany
    {
        return $this->belongsToMany(Ad::class, 'ad_devices');
    }

    public function station() :BelongsTo{
        return $this->belongsTo(Station::class,'station_id','id');
    }

    public function chargeBalanceGive()
    {
        return $this->belongsToMany(ChargeBalanceGive::class, 'give_devices');
    }

    /**
     * @param int $channelNumber
     * @param string $paymentType
     * @param int $chargeOptionId
     * @param User $user
     * @param int $couponRecordId
     * @return Order
     */
    public function createOrder(int $channelNumber, string $paymentType, int $chargeOptionId, $user = null, $couponRecordId = null)
    {
        $chargeOption = ChargeOption::findOrFail($chargeOptionId);
        $fee = $chargeOption->fee;

        if($couponRecordId) {
            $couponRecord = $user->couponRecords()->canUse()->find($couponRecordId);

            if(!$couponRecord) {
                abort(400, '优惠卷信息有误');
            }

            //优惠卷抵扣
            $fee -= $couponRecord->coupon->fee;

            $fee = max(0, $fee);
        }

        switch ($paymentType)
        {
            case Bill::PAYMENT_TYPE_BALANCE:
                if($fee > $user->getTotalBalance()) {
                    abort(300, '账户余额不足...');
                }
                break;

            case Bill::PAYMENT_TYPE_WE_CHAT_PAY:
            case Bill::PAYMENT_TYPE_ALI_PAY:
                //似乎没什么要检测的

                break;
        }

        //属于分销商 但是又不是该设备的可用充电选项
        if($chargeOption->distributor_id && !in_array($chargeOptionId, $this->getChargeOptions()->pluck('id')->toArray())) {
            abort(400, '当前选项不可用, 请刷新后重试!');
        }

        //检测设备是否在线
        if(!$this->checkIsOnline()) {
            abort(400, '设备不在线');
        }

        $usingOrder = $this->getUsingOrder($channelNumber);

        if($usingOrder) {
            abort(400, '该设备的该通道正在使用中, 请更换其他通道...');
        }

        $station = $this->station;
        if(!$station) {
            abort(400, '该设备未绑定站点..');
        }







        //创建订单
        $order = new Order();

        //赋值
        $order->sn = $this->sn;
        $order->payment_type = $paymentType;
        $order->estimate_stop_at = now()->addMinutes($chargeOption->minutes);
        $order->channel_number = $channelNumber;
        $order->number = rand_number($this->id);

        $order->fee = $fee;
        $order->original_fee = $chargeOption->fee;
        $order->minutes = $chargeOption->minutes;

        //关联关系
        $order->device()->associate($this);
        $order->station()->associate($station);
        $user && $order->user()->associate($user);
        $couponRecordId && $order->couponRecord()->associate($couponRecordId);

        $order->save();

        return $order;
    }


    public function updateUseRate()
    {
        $start = now()->subDay()->startOfDay();
        $end = now()->subDay()->endOfDay();

        $orderBuilder = $this->orders()->whereBetween('orders.created_at', [$start, $end])->effective();
        //昨天的有效订单
        $ordersCount = $orderBuilder->count();
        $totalFee = $orderBuilder->sum('fee');

        $useRate = round($ordersCount / $this->channel * 100, 2);

        logger('update use rate', [
            'orders_count' => $ordersCount,
            'total_fee' => $totalFee,
            'channel' => $this->channel,
        ]);

        if($useRate != $this->use_rate) {
            $this->use_rate = $useRate;
            $this->save();
        }

        $yesterday = now()->subDay()->toDateString();

        DeviceDailyRecord::setOne($this->id, 'use_rate', $useRate, $yesterday);
        DeviceDailyRecord::setOne($this->id, 'orders_count', $ordersCount, $yesterday);
        DeviceDailyRecord::setOne($this->id, 'total_fee', $totalFee, $yesterday);

        $firstDistributor = $this->getFirstDistributor();

        //不存在就不再继续了
        if(!$firstDistributor) {
            return;
        }

        //设备已经被采购  或者已经满足退押金了 就不再深入判断了
        if($this->is_purchase || $this->can_refund_deposit_at) {
            return;
        }

        //更新完设备后 检测是否满足退押金的条件
        $useRate30Cont = $this->dailyRecordsUseRate30()->where('created_at', '>=', $firstDistributor->pivot->created_at)->count();
        $useRate20Cont = $this->dailyRecordsUseRate20()->where('created_at', '>=', $firstDistributor->pivot->created_at)->count();

        //使用率大于30满足180天 或者使用率大于20满足360天 则满足退押金
        if($useRate30Cont >= 180 || $useRate20Cont >= 360) {

            $deposit = $this->deposit;
            $this->can_refund_deposit_at = now();
            $this->refund_deposit_at = now();
            $this->save();
            //退款
            $firstDistributor->increment('balance', $deposit);

            Bill::record(
                $firstDistributor,
                $deposit,
                Bill::TYPE_DEVICE_JOIN_DEPOSIT_REFUND,
                Bill::PAYMENT_TYPE_SYSTEM,
                $this->id,
                "设备 {$this->sn} 押金退回"
            );
        }
    }

    public function scopeIsOnline($query)
    {
        return $query->where('last_online_at', '>=', now()->subMinutes(11));
    }

    public function scopeIsOffline($query)
    {
        return $query->where('last_online_at', '<', now()->subMinutes(11));
    }

    public function checkIsOnline()
    {
        return $this->last_online_at && now()->subMinutes(11)->lt($this->last_online_at);
    }


    public function changeToActive()
    {
        $this->active_at = Carbon::now();
        $this->expires_at = Carbon::now()->addYear(5);
        $this->save();
    }

    public function bindToDistributor(Distributor $handleDistributor, Distributor $bindDistributor = null)
    {
        $bindDistributor = $bindDistributor ?? $handleDistributor;

        $lastDistributor = $this->getLastDistributor();

        $handleDistributorAndAncestorIds = $handleDistributor->ancestors->pluck('id')->merge([$handleDistributor->id])->toArray();

        //绑定过代理   但是最底层不属于操作人(或者他的上级) 则不能自己绑定
        if($lastDistributor && !in_array($lastDistributor->id, $handleDistributorAndAncestorIds)) {
            abort(400, "设备编号有误或无权限操作, sn:" . $this->sn);
        }
        //已经绑定了的分销商id些
        $bindDistributorIds = $this->distributors->pluck('id')->toArray();

        //绑定人的上级和绑定人自己 都需要绑定这个设备
        $bindDistributor->ancestors->merge(collect([$bindDistributor]))->map(function ($distributor) use ($bindDistributorIds) {
            //已经绑定过的就跳过
            if(in_array($distributor->id, $bindDistributorIds)) {
                return;
            }

            $this->distributors()->attach($distributor);

        });
    }

    public function getStationOrFail()
    {
        if(!$this->station) {
            abort(400, '设备不可用, 该设备尚未投放到站点!');
        }

        return $this->station;
    }
//
//    public function checkIsOnline()
//    {
//        //当前时间 回退13分钟 小于最后在线时间 则说明在线
//        return now()->subMinutes(13)->lt($this->last_online_at);
//    }

    public function orders()
    {
        return $this->hasMany(Order::class,'device_id','id');
    }


    public function faultLogs()
    {
        return $this->hasMany(DeviceFaultLog::class);
    }


    public function getUsingOrder($channelNumber)
    {
        return $this->orders()->inUsing()->where('channel_number', $channelNumber)->first();
    }

    public function dailyRecords()
    {
        return $this->hasMany(DeviceDailyRecord::class);
    }


    public function dailyRecordsUseRate30()
    {
        return $this->dailyRecords()->where('use_rate', '>=', 30);
    }


    public function dailyRecordsUseRate20()
    {
        return $this->dailyRecords()->where('use_rate', '>=', 20);
    }


    /**
     * @return string
     */
    public static function getDistributorRelatePivotTable()
    {
        return 'distributor_devices';
    }

    /**
     * @return array
     */
    public static function getDistributorRelatePivotColumns()
    {
        return ['profit', 'in_storeroom'];
    }

    /**
     * @return string
     */
    public static function getDistributorRelatePivotModel()
    {
        return DistributorDevice::class;
    }

    public function start($channelNumber, $minutes, $fee)
    {
        IotService::start($this->iot_sn, $channelNumber, $minutes, $fee);
    }


    public static function addStation(string $stationId, $longitude,$latitude): int
    {
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\Redis::class);
        return $redis->geoAdd(self::GEO_KEY, $longitude, $latitude, $stationId);
    }

    public static function getGeoDist($lng,$lat,$stationId){
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\Redis::class);
        $distance = $redis->geoDist(self::GEO_KEY, $lng, $lat, $stationId, 'km');
        return $distance;
    }

    public static function queryNearbyStations($longitude,$latitude, $radius, int $count = 999): array
    {
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\Redis::class);
        $options = ['WITHDIST','WITHCOORD'];
        $result = $redis->geoRadius(self::GEO_KEY, $longitude, $latitude, $radius, 'km',$options);
        return $result;
    }

    public static function removeStation(string $stationId): int
    {
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\Redis::class);
        // 注意：这里应该是 GEOREM 而不是 GEOREM，后者可能是个打字错误
        return $redis->geoRem(self::GEO_KEY, $stationId);
    }

    public static function updateStation(string $stationId, float $newLongitude, float $newLatitude): bool
    {
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\Redis::class);
        // 首先，尝试从Geo集合中移除旧的站点
        $removed = $redis->geoRem(self::GEO_KEY, $stationId);

        // 如果成功移除了站点，则添加新的经纬度
        if ($removed === 1) {
            $added = $redis->geoAdd(self::GEO_KEY, $newLongitude, $newLatitude, $stationId);
            return $added === 1; // 返回是否成功添加
        }

        // 如果没有找到要删除的站点，则返回false
        return false;
    }


}
